/********************************************************************************/
/*										*/
/*			Interfaces to the Crypto Engine				*/
/*			     Written by Ken Goldman				*/
/*		       IBM Thomas J. Watson Research Center			*/
/*										*/
/*  Licenses and Notices							*/
/*										*/
/*  1. Copyright Licenses:							*/
/*										*/
/*  - Trusted Computing Group (TCG) grants to the user of the source code in	*/
/*    this specification (the "Source Code") a worldwide, irrevocable, 		*/
/*    nonexclusive, royalty free, copyright license to reproduce, create 	*/
/*    derivative works, distribute, display and perform the Source Code and	*/
/*    derivative works thereof, and to grant others the rights granted herein.	*/
/*										*/
/*  - The TCG grants to the user of the other parts of the specification 	*/
/*    (other than the Source Code) the rights to reproduce, distribute, 	*/
/*    display, and perform the specification solely for the purpose of 		*/
/*    developing products based on such documents.				*/
/*										*/
/*  2. Source Code Distribution Conditions:					*/
/*										*/
/*  - Redistributions of Source Code must retain the above copyright licenses, 	*/
/*    this list of conditions and the following disclaimers.			*/
/*										*/
/*  - Redistributions in binary form must reproduce the above copyright 	*/
/*    licenses, this list of conditions	and the following disclaimers in the 	*/
/*    documentation and/or other materials provided with the distribution.	*/
/*										*/
/*  3. Disclaimers:								*/
/*										*/
/*  - THE COPYRIGHT LICENSES SET FORTH ABOVE DO NOT REPRESENT ANY FORM OF	*/
/*  LICENSE OR WAIVER, EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE, WITH	*/
/*  RESPECT TO PATENT RIGHTS HELD BY TCG MEMBERS (OR OTHER THIRD PARTIES)	*/
/*  THAT MAY BE NECESSARY TO IMPLEMENT THIS SPECIFICATION OR OTHERWISE.		*/
/*  Contact TCG Administration (admin@trustedcomputinggroup.org) for 		*/
/*  information on specification licensing rights available through TCG 	*/
/*  membership agreements.							*/
/*										*/
/*  - THIS SPECIFICATION IS PROVIDED "AS IS" WITH NO EXPRESS OR IMPLIED 	*/
/*    WARRANTIES WHATSOEVER, INCLUDING ANY WARRANTY OF MERCHANTABILITY OR 	*/
/*    FITNESS FOR A PARTICULAR PURPOSE, ACCURACY, COMPLETENESS, OR 		*/
/*    NONINFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS, OR ANY WARRANTY 		*/
/*    OTHERWISE ARISING OUT OF ANY PROPOSAL, SPECIFICATION OR SAMPLE.		*/
/*										*/
/*  - Without limitation, TCG and its members and licensors disclaim all 	*/
/*    liability, including liability for infringement of any proprietary 	*/
/*    rights, relating to use of information in this specification and to the	*/
/*    implementation of this specification, and TCG disclaims all liability for	*/
/*    cost of procurement of substitute goods or services, lost profits, loss 	*/
/*    of use, loss of data or any incidental, consequential, direct, indirect, 	*/
/*    or special damages, whether under contract, tort, warranty or otherwise, 	*/
/*    arising in any way out of use or reliance upon this specification or any 	*/
/*    information herein.							*/
/*										*/
/*  (c) Copyright IBM Corp. and others, 2016 - 2023				*/
/*										*/
/********************************************************************************/

//** Introduction
//
//  This module contains the interfaces to the CryptoEngine and provides
//  miscellaneous cryptographic functions in support of the TPM.
//

//** Includes
#include "Tpm.h"
#include "Marshal.h"

//****************************************************************************/
//**     Hash/HMAC Functions
//****************************************************************************/

//*** CryptHmacSign()
// Sign a digest using an HMAC key. This an HMAC of a digest, not an HMAC of a
// message.
//  Return Type: TPM_RC
//      TPM_RC_HASH         not a valid hash
static TPM_RC CryptHmacSign(TPMT_SIGNATURE* signature,  // OUT: signature
			    OBJECT*         signKey,    // IN: HMAC key sign the hash
			    TPM2B_DIGEST*   hashData    // IN: hash to be signed
			    )
{
    HMAC_STATE hmacState;
    UINT32     digestSize;

    digestSize = CryptHmacStart2B(&hmacState,
				  signature->signature.any.hashAlg,
				  &signKey->sensitive.sensitive.bits.b);
    CryptDigestUpdate2B(&hmacState.hashState, &hashData->b);
    CryptHmacEnd(&hmacState, digestSize, (BYTE*)&signature->signature.hmac.digest);
    return TPM_RC_SUCCESS;
}

//*** CryptHMACVerifySignature()
// This function will verify a signature signed by a HMAC key.
// Note that a caller needs to prepare 'signature' with the signature algorithm
// (TPM_ALG_HMAC) and the hash algorithm to use. This function then builds a
// signature of that type.
//  Return Type: TPM_RC
//      TPM_RC_SCHEME           not the proper scheme for this key type
//      TPM_RC_SIGNATURE        if invalid input or signature is not genuine
static TPM_RC CryptHMACVerifySignature(
				       OBJECT*         signKey,   // IN: HMAC key signed the hash
				       TPM2B_DIGEST*   hashData,  // IN: digest being verified
				       TPMT_SIGNATURE* signature  // IN: signature to be verified
				       )
{
    TPMT_SIGNATURE         test;
    TPMT_KEYEDHASH_SCHEME* keyScheme =
	&signKey->publicArea.parameters.keyedHashDetail.scheme;
    //
    if((signature->sigAlg != TPM_ALG_HMAC)
       || (signature->signature.hmac.hashAlg == TPM_ALG_NULL))
	return TPM_RC_SCHEME;
    // This check is not really needed for verification purposes. However, it does
    // prevent someone from trying to validate a signature using a weaker hash
    // algorithm than otherwise allowed by the key. That is, a key with a scheme
    // other than TMP_ALG_NULL can only be used to validate signatures that have
    // a matching scheme.
    if((keyScheme->scheme != TPM_ALG_NULL)
       && ((keyScheme->scheme != signature->sigAlg)
	   || (keyScheme->details.hmac.hashAlg != signature->signature.any.hashAlg)))
	return TPM_RC_SIGNATURE;
    test.sigAlg                 = signature->sigAlg;
    test.signature.hmac.hashAlg = signature->signature.hmac.hashAlg;

    CryptHmacSign(&test, signKey, hashData);

    // Compare digest
    if(!MemoryEqual(&test.signature.hmac.digest,
		    &signature->signature.hmac.digest,
		    CryptHashGetDigestSize(signature->signature.any.hashAlg)))
	return TPM_RC_SIGNATURE;

    return TPM_RC_SUCCESS;
}

//*** CryptGenerateKeyedHash()
// This function creates a keyedHash object.
// Return type: TPM_RC
//      TPM_RC_NO_RESULT    cannot get values from random number generator
//      TPM_RC_SIZE         sensitive data size is larger than allowed for
//                          the scheme
static TPM_RC CryptGenerateKeyedHash(
				     TPMT_PUBLIC* publicArea,                 // IN/OUT: the public area template
				     //     for the new key.
				     TPMT_SENSITIVE*        sensitive,        // OUT: sensitive area
				     TPMS_SENSITIVE_CREATE* sensitiveCreate,  // IN: sensitive creation data
				     RAND_STATE*            rand              // IN: "entropy" source
				     )
{
    TPMT_KEYEDHASH_SCHEME* scheme;
    TPM_ALG_ID             hashAlg;
    UINT16                 digestSize;

    scheme = &publicArea->parameters.keyedHashDetail.scheme;

    if(publicArea->type != TPM_ALG_KEYEDHASH)
	return TPM_RC_FAILURE;

    // Pick the limiting hash algorithm
    if(scheme->scheme == TPM_ALG_NULL)
	hashAlg = publicArea->nameAlg;
    else if(scheme->scheme == TPM_ALG_XOR)
	hashAlg = scheme->details.xorr.hashAlg;
    else
	hashAlg = scheme->details.hmac.hashAlg;
    digestSize = CryptHashGetDigestSize(hashAlg);

    // if this is a signing or a decryption key, then the limit
    // for the data size is the block size of the hash. This limit
    // is set because larger values have lower entropy because of the
    // HMAC function. The lower limit is 1/2 the size of the digest
    //
    //If the user provided the key, check that it is a proper size
    if(sensitiveCreate->data.t.size != 0)
	{
	    if(IS_ATTRIBUTE(publicArea->objectAttributes, TPMA_OBJECT, decrypt)
	       || IS_ATTRIBUTE(publicArea->objectAttributes, TPMA_OBJECT, sign))
		{
		    if(sensitiveCreate->data.t.size > CryptHashGetBlockSize(hashAlg))
			return TPM_RC_SIZE;
#if 0  // May make this a FIPS-mode requirement
		    if(sensitiveCreate->data.t.size < (digestSize / 2))
			return TPM_RC_SIZE;
#endif
		}
	    // If this is a data blob, then anything that will get past the unmarshaling
	    // is OK
	    MemoryCopy2B(&sensitive->sensitive.bits.b,
			 &sensitiveCreate->data.b,
			 sizeof(sensitive->sensitive.bits.t.buffer));
	}
    else
	{
	    // The TPM is going to generate the data so set the size to be the
	    // size of the digest of the algorithm
	    sensitive->sensitive.bits.t.size =
		DRBG_Generate(rand, sensitive->sensitive.bits.t.buffer, digestSize);
	    if(sensitive->sensitive.bits.t.size == 0)
		return (g_inFailureMode) ? TPM_RC_FAILURE : TPM_RC_NO_RESULT;
	}
    return TPM_RC_SUCCESS;
}

//*** CryptIsSchemeAnonymous()
// This function is used to test a scheme to see if it is an anonymous scheme
// The only anonymous scheme is ECDAA. ECDAA can be used to do things
// like U-Prove.
BOOL CryptIsSchemeAnonymous(TPM_ALG_ID scheme  // IN: the scheme algorithm to test
			    )
{
    return scheme == TPM_ALG_ECDAA;
}

//**** ************************************************************************
//** Symmetric Functions
//**** ************************************************************************

//*** ParmDecryptSym()
//  This function performs parameter decryption using symmetric block cipher.
/*(See Part 1 specification)
// Symmetric parameter decryption
//      When parameter decryption uses a symmetric block cipher, a decryption
//      key and IV will be generated from:
//      KDFa(hash, sessionAuth, "CFB", nonceNewer, nonceOlder, bits)    (24)
//      Where:
//      hash            the hash function associated with the session
//      sessionAuth     the sessionAuth associated with the session
//      nonceNewer      nonceCaller for a command
//      nonceOlder      nonceTPM for a command
//      bits            the number of bits required for the symmetric key
//                      plus an IV
*/
void ParmDecryptSym(TPM_ALG_ID symAlg,         // IN: the symmetric algorithm
		    TPM_ALG_ID hash,           // IN: hash algorithm for KDFa
		    UINT16     keySizeInBits,  // IN: the key size in bits
		    TPM2B*     key,            // IN: KDF HMAC key
		    TPM2B*     nonceCaller,    // IN: nonce caller
		    TPM2B*     nonceTpm,       // IN: nonce TPM
		    UINT32     dataSize,       // IN: size of parameter buffer
		    BYTE*      data            // OUT: buffer to be decrypted
		    )
{
    // KDF output buffer
    // It contains parameters for the CFB encryption
    // From MSB to LSB, they are the key and iv
    BYTE symParmString[MAX_SYM_KEY_BYTES + MAX_SYM_BLOCK_SIZE];
    // Symmetric key size in byte
    UINT16   keySize = (keySizeInBits + 7) / 8;
    TPM2B_IV iv;

    iv.t.size = CryptGetSymmetricBlockSize(symAlg, keySizeInBits);
    // If there is decryption to do...
    if(iv.t.size > 0)
	{
	    // Generate key and iv
	    CryptKDFa(hash,
		      key,
		      CFB_KEY,
		      nonceCaller,
		      nonceTpm,
		      keySizeInBits + (iv.t.size * 8),
		      symParmString,
		      NULL,
		      FALSE);
	    MemoryCopy(iv.t.buffer, &symParmString[keySize], iv.t.size);

	    CryptSymmetricDecrypt(data,
				  symAlg,
				  keySizeInBits,
				  symParmString,
				  &iv,
				  TPM_ALG_CFB,
				  dataSize,
				  data);
	}
    return;
}

//*** ParmEncryptSym()
//  This function performs parameter encryption using symmetric block cipher.
/*(See part 1 specification)
//      When parameter decryption uses a symmetric block cipher, an encryption
//      key and IV will be generated from:
//      KDFa(hash, sessionAuth, "CFB", nonceNewer, nonceOlder, bits)    (24)
//      Where:
//      hash            the hash function associated with the session
//      sessionAuth     the sessionAuth associated with the session
//      nonceNewer      nonceTPM for a response
//      nonceOlder      nonceCaller for a response
//      bits            the number of bits required for the symmetric key
//                      plus an IV
*/
void ParmEncryptSym(TPM_ALG_ID symAlg,         // IN: symmetric algorithm
		    TPM_ALG_ID hash,           // IN: hash algorithm for KDFa
		    UINT16     keySizeInBits,  // IN: symmetric key size in bits
		    TPM2B*     key,            // IN: KDF HMAC key
		    TPM2B*     nonceCaller,    // IN: nonce caller
		    TPM2B*     nonceTpm,       // IN: nonce TPM
		    UINT32     dataSize,       // IN: size of parameter buffer
		    BYTE*      data            // OUT: buffer to be encrypted
		    )
{
    // KDF output buffer
    // It contains parameters for the CFB encryption
    BYTE symParmString[MAX_SYM_KEY_BYTES + MAX_SYM_BLOCK_SIZE];

    // Symmetric key size in bytes
    UINT16   keySize = (keySizeInBits + 7) / 8;

    TPM2B_IV iv;

    iv.t.size = CryptGetSymmetricBlockSize(symAlg, keySizeInBits);
    // See if there is any encryption to do
    if(iv.t.size > 0)
	{
	    // Generate key and iv
	    CryptKDFa(hash,
		      key,
		      CFB_KEY,
		      nonceTpm,
		      nonceCaller,
		      keySizeInBits + (iv.t.size * 8),
		      symParmString,
		      NULL,
		      FALSE);
	    MemoryCopy(iv.t.buffer, &symParmString[keySize], iv.t.size);

	    CryptSymmetricEncrypt(data,
				  symAlg,
				  keySizeInBits,
				  symParmString,
				  &iv,
				  TPM_ALG_CFB,
				  dataSize,
				  data);
	}
    return;
}

//*** CryptGenerateKeySymmetric()
// This function generates a symmetric cipher key. The derivation process is
// determined by the type of the provided 'rand'
// Return type: TPM_RC
//      TPM_RC_NO_RESULT    cannot get a random value
//      TPM_RC_KEY_SIZE     key size in the public area does not match the size
//                          in the sensitive creation area
//      TPM_RC_KEY          provided key value is not allowed
static TPM_RC CryptGenerateKeySymmetric(
					TPMT_PUBLIC* publicArea,                 // IN/OUT: The public area template
					//     for the new key.
					TPMT_SENSITIVE*        sensitive,        // OUT: sensitive area
					TPMS_SENSITIVE_CREATE* sensitiveCreate,  // IN: sensitive creation data
					RAND_STATE*            rand              // IN: the "entropy" source for
					)
{
    UINT16 keyBits = publicArea->parameters.symDetail.sym.keyBits.sym;
    TPM_RC result;
    //
    // only do multiples of RADIX_BITS
    if((keyBits % RADIX_BITS) != 0)
	return TPM_RC_KEY_SIZE;
    // If this is not a new key, then the provided key data must be the right size
    if(sensitiveCreate->data.t.size != 0)
	{
	    result = CryptSymKeyValidate(&publicArea->parameters.symDetail.sym,
					 (TPM2B_SYM_KEY*)&sensitiveCreate->data);
	             if(result == TPM_RC_SUCCESS)
			 MemoryCopy2B(&sensitive->sensitive.sym.b,
				      &sensitiveCreate->data.b,
				      sizeof(sensitive->sensitive.sym.t.buffer));
	}
    else
	{
	    sensitive->sensitive.sym.t.size = DRBG_Generate(
							    rand, sensitive->sensitive.sym.t.buffer, BITS_TO_BYTES(keyBits));
	    if(g_inFailureMode)
		result = TPM_RC_FAILURE;
	    else if(sensitive->sensitive.sym.t.size == 0)
		result = TPM_RC_NO_RESULT;
	    else
		result = TPM_RC_SUCCESS;
	}
    return result;
}

//*** CryptXORObfuscation()
// This function implements XOR obfuscation. It should not be called if the
// hash algorithm is not implemented. The only return value from this function
// is TPM_RC_SUCCESS.
void CryptXORObfuscation(TPM_ALG_ID hash,      // IN: hash algorithm for KDF
			 TPM2B*     key,       // IN: KDF key
			 TPM2B*     contextU,  // IN: contextU
			 TPM2B*     contextV,  // IN: contextV
			 UINT32     dataSize,  // IN: size of data buffer
			 BYTE*      data       // IN/OUT: data to be XORed in place
			 )
{
    BYTE   mask[MAX_DIGEST_SIZE];  // Allocate a digest sized buffer
    BYTE*  pm;
    UINT32 i;
    UINT32 counter     = 0;
    UINT16 hLen        = CryptHashGetDigestSize(hash);
    UINT32 requestSize = dataSize * 8;
    INT32  remainBytes = (INT32)dataSize;

    pAssert((key != NULL) && (data != NULL) && (hLen != 0));

    // Call KDFa to generate XOR mask
    for(; remainBytes > 0; remainBytes -= hLen)
	{
	    // Make a call to KDFa to get next iteration
	    CryptKDFa(hash,
		      key,
		      XOR_KEY,
		      contextU,
		      contextV,
		      requestSize,
		      mask,
		      &counter,
		      TRUE);

	    // XOR next piece of the data
	    pm = mask;
	    for(i = hLen < remainBytes ? hLen : remainBytes; i > 0; i--)
		*data++ ^= *pm++;
	}
    return;
}

//****************************************************************************
//** Initialization and shut down
//****************************************************************************

//*** CryptInit()
// This function is called when the TPM receives a _TPM_Init indication.
//
// NOTE: The hash algorithms do not have to be tested, they just need to be
// available. They have to be tested before the TPM can accept HMAC authorization
// or return any result that relies on a hash algorithm.
//  Return Type: BOOL
//      TRUE(1)         initializations succeeded
//      FALSE(0)        initialization failed and caller should place the TPM into
//                      Failure Mode
BOOL CryptInit(void)
{
    BOOL ok;
    // Initialize the vector of implemented algorithms
    AlgorithmGetImplementedVector(&g_implementedAlgorithms);

    // Indicate that all test are necessary
    CryptInitializeToTest();

    // Do any library initializations that are necessary. If any fails,
    // the caller should go into failure mode;
    ok = ExtMath_LibInit();
    ok = ok && CryptSymInit();
    ok = ok && CryptRandInit();
    ok = ok && CryptHashInit();
#if ALG_RSA
    ok = ok && CryptRsaInit();
#endif  // ALG_RSA
#if ALG_ECC
    ok = ok && CryptEccInit();
#endif  // ALG_ECC
    return ok;
}

//*** CryptStartup()
// This function is called by TPM2_Startup() to initialize the functions in
// this cryptographic library and in the provided CryptoLibrary. This function
// and CryptUtilInit() are both provided so that the implementation may move the
// initialization around to get the best interaction.
//  Return Type: BOOL
//      TRUE(1)         startup succeeded
//      FALSE(0)        startup failed and caller should place the TPM into
//                      Failure Mode
BOOL CryptStartup(STARTUP_TYPE type  // IN: the startup type
		  )
{
    BOOL OK;
    NOT_REFERENCED(type);

    OK = CryptSymStartup() && CryptRandStartup() && CryptHashStartup()
#if ALG_RSA
	 && CryptRsaStartup()
#endif  // ALG_RSA
#if ALG_ECC
	 && CryptEccStartup()
#endif  // ALG_ECC
	 ;
#if ALG_ECC
    // Don't directly check for SU_RESET because that is the default
    if(OK && (type != SU_RESTART) && (type != SU_RESUME))
	{
	    // If the shutdown was orderly, then the values recovered from NV will
	    // be OK to use.
	    // Get a new  random commit nonce
	    gr.commitNonce.t.size = sizeof(gr.commitNonce.t.buffer);
	    CryptRandomGenerate(gr.commitNonce.t.size, gr.commitNonce.t.buffer);
	    // Reset the counter and commit array
	    gr.commitCounter = 0;
	    MemorySet(gr.commitArray, 0, sizeof(gr.commitArray));
	}
#endif  // ALG_ECC
    return OK;
}

//****************************************************************************
//** Algorithm-Independent Functions
//****************************************************************************
//*** Introduction
// These functions are used generically when a function of a general type
// (e.g., symmetric encryption) is required.  The functions will modify the
// parameters as required to interface to the indicated algorithms.
//
//*** CryptIsAsymAlgorithm()
// This function indicates if an algorithm is an asymmetric algorithm.
//  Return Type: BOOL
//      TRUE(1)         if it is an asymmetric algorithm
//      FALSE(0)        if it is not an asymmetric algorithm
BOOL CryptIsAsymAlgorithm(TPM_ALG_ID algID  // IN: algorithm ID
			  )
{
    switch(algID)
	{
#if ALG_RSA
	  case TPM_ALG_RSA:
#endif
#if ALG_ECC
	  case TPM_ALG_ECC:
#endif
	    return TRUE;
	    break;
	  default:
	    break;
	}
    return FALSE;
}

//*** CryptSecretEncrypt()
// This function creates a secret value and its associated secret structure using
// an asymmetric algorithm.
//
// This function is used by TPM2_Rewrap() TPM2_MakeCredential(),
// and TPM2_Duplicate().
//  Return Type: TPM_RC
//      TPM_RC_ATTRIBUTES   'keyHandle' does not reference a valid decryption key
//      TPM_RC_KEY          invalid ECC key (public point is not on the curve)
//      TPM_RC_SCHEME       RSA key with an unsupported padding scheme
//      TPM_RC_VALUE        numeric value of the data to be decrypted is greater
//                          than the RSA key modulus
TPM_RC
CryptSecretEncrypt(OBJECT*      encryptKey,  // IN: encryption key object
		   const TPM2B* label,       // IN: a null-terminated string as L
		   TPM2B_DATA*  data,        // OUT: secret value
		   TPM2B_ENCRYPTED_SECRET* secret  // OUT: secret structure
		   )
{
    TPM_RC result = TPM_RC_SUCCESS;
    //
    if(data == NULL || secret == NULL)
	return TPM_RC_FAILURE;

    // CryptKDFe was fixed to not add a NULL byte as per NIST.SP.800-56Cr2.pdf
    // (required for ACVP tests). This check ensures backwards compatibility with
    // previous versions of the TPM reference code by verifying the label itself
    // has a NULL terminator.  Note the TPM spec specifies that the label must be NULL
    // terminated.  This is only a "new" failure path in the sense that it adds a
    // runtime check of hardcoded constants; provided the code is correct it will never
    // fail, and running the compliance tests will verify this isn't hit.
    if((label == NULL) || (label->size == 0) || (label->buffer[label->size - 1] != 0))
	return TPM_RC_FAILURE;

    // The output secret value has the size of the digest produced by the nameAlg.
    data->t.size = CryptHashGetDigestSize(encryptKey->publicArea.nameAlg);

    if(!IS_ATTRIBUTE(encryptKey->publicArea.objectAttributes, TPMA_OBJECT, decrypt))
	return TPM_RC_ATTRIBUTES;
    switch(encryptKey->publicArea.type)
	{
#if ALG_RSA
	  case TPM_ALG_RSA:
	      {
		  // The encryption scheme is OAEP using the nameAlg of the encrypt key.
		  TPMT_RSA_DECRYPT scheme;
		  scheme.scheme                 = TPM_ALG_OAEP;
		  scheme.details.anySig.hashAlg = encryptKey->publicArea.nameAlg;

		  // Create secret data from RNG
		  CryptRandomGenerate(data->t.size, data->t.buffer);

		  // Encrypt the data by RSA OAEP into encrypted secret
		  result = CryptRsaEncrypt((TPM2B_PUBLIC_KEY_RSA*)secret,
					   &data->b,
					   encryptKey,
					   &scheme,
					   label,
					   NULL);
	      }
	      break;
#endif  // ALG_RSA

#if ALG_ECC
	  case TPM_ALG_ECC:
	      {
		  TPMS_ECC_POINT      eccPublic;
		  TPM2B_ECC_PARAMETER eccPrivate;
		  TPMS_ECC_POINT      eccSecret;
		  BYTE*               buffer = secret->t.secret;

		  // Need to make sure that the public point of the key is on the
		  // curve defined by the key.
		  if(!CryptEccIsPointOnCurve(
					     encryptKey->publicArea.parameters.eccDetail.curveID,
					     &encryptKey->publicArea.unique.ecc))
		      result = TPM_RC_KEY;
		  else
		      {
			  // Call crypto engine to create an auxiliary ECC key
			  // We assume crypt engine initialization should always success.
			  // Otherwise, TPM should go to failure mode.

			  CryptEccNewKeyPair(
					     &eccPublic,
					     &eccPrivate,
					     encryptKey->publicArea.parameters.eccDetail.curveID);
			  // Marshal ECC public to secret structure. This will be used by the
			  // recipient to decrypt the secret with their private key.
			  secret->t.size = TPMS_ECC_POINT_Marshal(&eccPublic, &buffer, NULL);

			  // Compute ECDH shared secret which is R = [d]Q where d is the
			  // private part of the ephemeral key and Q is the public part of a
			  // TPM key. TPM_RC_KEY error return from CryptComputeECDHSecret
			  // because the auxiliary ECC key is just created according to the
			  // parameters of input ECC encrypt key.
			  if(CryptEccPointMultiply(
						   &eccSecret,
						   encryptKey->publicArea.parameters.eccDetail.curveID,
						   &encryptKey->publicArea.unique.ecc,
						   &eccPrivate,
						   NULL,
						   NULL)
			     != TPM_RC_SUCCESS)
			      result = TPM_RC_KEY;
			  else
			      {
				  // The secret value is computed from Z using KDFe as:
				  // secret := KDFe(HashID, Z, Use, PartyUInfo, PartyVInfo, bits)
				  // Where:
				  //  HashID  the nameAlg of the decrypt key
				  //  Z   the x coordinate (Px) of the product (P) of the point
				  //      (Q) of the secret and the private x coordinate (de,V)
				  //      of the decryption key
				  //  Use a null-terminated string containing "SECRET"
				  //  PartyUInfo  the x coordinate of the point in the secret
				  //              (Qe,U )
				  //  PartyVInfo  the x coordinate of the public key (Qs,V )
				  //  bits    the number of bits in the digest of HashID
				  // Retrieve seed from KDFe
				  CryptKDFe(encryptKey->publicArea.nameAlg,
					    &eccSecret.x.b,
					    label,
					    &eccPublic.x.b,
					    &encryptKey->publicArea.unique.ecc.x.b,
					    data->t.size * 8,
					    data->t.buffer);
			      }
		      }
	      }
	      break;
#endif  // ALG_ECC
	  default:
	    FAIL(FATAL_ERROR_INTERNAL);
	    break;
	}
    return result;
}

//*** CryptSecretDecrypt()
// Decrypt a secret value by asymmetric (or symmetric) algorithm
// This function is used for ActivateCredential and Import for asymmetric
// decryption, and StartAuthSession for both asymmetric and symmetric
// decryption process
//
//  Return Type: TPM_RC
//      TPM_RC_ATTRIBUTES        RSA key is not a decryption key
//      TPM_RC_BINDING           Invalid RSA key (public and private parts are not
//                               cryptographically bound.
//      TPM_RC_ECC_POINT         ECC point in the secret is not on the curve
//      TPM_RC_INSUFFICIENT      failed to retrieve ECC point from the secret
//      TPM_RC_NO_RESULT         multiplication resulted in ECC point at infinity
//      TPM_RC_SIZE              data to decrypt is not of the same size as RSA key
//      TPM_RC_VALUE             For RSA key, numeric value of the encrypted data is
//                               greater than the modulus, or the recovered data is
//                               larger than the output buffer.
//                               For keyedHash or symmetric key, the secret is
//                               larger than the size of the digest produced by
//                               the name algorithm.
//      TPM_RC_FAILURE           internal error
TPM_RC
CryptSecretDecrypt(OBJECT*      decryptKey,   // IN: decrypt key
		   TPM2B_NONCE* nonceCaller,  // IN: nonceCaller.  It is needed for
		   //     symmetric decryption.  For
		   //     asymmetric decryption, this
		   //     parameter is NULL
		   const TPM2B*            label,   // IN: a value for L
		   TPM2B_ENCRYPTED_SECRET* secret,  // IN: input secret
		   TPM2B_DATA*             data     // OUT: decrypted secret value
		   )
{
    TPM_RC result = TPM_RC_SUCCESS;

    // CryptKDFe was fixed to not add a NULL byte as per NIST.SP.800-56Cr2.pdf
    // (required for ACVP tests). This check ensures backwards compatibility with
    // previous versions of the TPM reference code by verifying the label itself
    // has a NULL terminator.  Note the TPM spec specifies that the label must be NULL
    // terminated.  This is only a "new" failure path in the sense that it adds a
    // runtime check of hardcoded constants; provided the code is correct it will never
    // fail, and running the compliance tests will verify this isn't hit.
    if((label == NULL) || (label->size == 0) || (label->buffer[label->size - 1] != 0))
	return TPM_RC_FAILURE;

    // Decryption for secret
    switch(decryptKey->publicArea.type)
	{
#if ALG_RSA
	  case TPM_ALG_RSA:
	      {
		  TPMT_RSA_DECRYPT scheme;
		  TPMT_RSA_SCHEME* keyScheme =
		      &decryptKey->publicArea.parameters.rsaDetail.scheme;
		  UINT16 digestSize;

		  scheme = *(TPMT_RSA_DECRYPT*)keyScheme;
		  // If the key scheme is TPM_ALG_NULL, set the scheme to OAEP and
		  // set the algorithm to the name algorithm.
		  if(scheme.scheme == TPM_ALG_NULL)
		      {
			  // Use OAEP scheme
			  scheme.scheme               = TPM_ALG_OAEP;
			  scheme.details.oaep.hashAlg = decryptKey->publicArea.nameAlg;
		      }
		  // use the digestSize as an indicator of whether or not the scheme
		  // is using a supported hash algorithm.
		  // Note: depending on the scheme used for encryption, a hashAlg might
		  // not be needed. However, the return value has to have some upper
		  // limit on the size. In this case, it is the size of the digest of the
		  // hash algorithm. It is checked after the decryption is done but, there
		  // is no point in doing the decryption if the size is going to be
		  // 'wrong' anyway.
		  digestSize = CryptHashGetDigestSize(scheme.details.oaep.hashAlg);
		  if(scheme.scheme != TPM_ALG_OAEP || digestSize == 0)
		      return TPM_RC_SCHEME;

		  // Set the output buffer capacity
		  data->t.size = sizeof(data->t.buffer);

		  // Decrypt seed by RSA OAEP
		  result =
		      CryptRsaDecrypt(&data->b, &secret->b, decryptKey, &scheme, label);
		  if((result == TPM_RC_SUCCESS) && (data->t.size > digestSize))
		      result = TPM_RC_VALUE;
	      }
	      break;
#endif  // ALG_RSA
#if ALG_ECC
	  case TPM_ALG_ECC:
	      {
		  TPMS_ECC_POINT eccPublic;
		  TPMS_ECC_POINT eccSecret;
		  BYTE*          buffer = secret->t.secret;
		  INT32          size   = secret->t.size;

		  // Retrieve ECC point from secret buffer
		  result = TPMS_ECC_POINT_Unmarshal(&eccPublic, &buffer, &size);
		  if(result == TPM_RC_SUCCESS)
		      {
			  result = CryptEccPointMultiply(
							 &eccSecret,
							 decryptKey->publicArea.parameters.eccDetail.curveID,
							 &eccPublic,
							 &decryptKey->sensitive.sensitive.ecc,
							 NULL,
							 NULL);
			  if(result == TPM_RC_SUCCESS)
			      {
				  // Set the size of the "recovered" secret value to be the size
				  // of the digest produced by the nameAlg.
				  data->t.size =
				      CryptHashGetDigestSize(decryptKey->publicArea.nameAlg);

				  // The secret value is computed from Z using KDFe as:
				  // secret := KDFe(HashID, Z, Use, PartyUInfo, PartyVInfo, bits)
				  // Where:
				  //  HashID -- the nameAlg of the decrypt key
				  //  Z --  the x coordinate (Px) of the product (P) of the point
				  //        (Q) of the secret and the private x coordinate (de,V)
				  //        of the decryption key
				  //  Use -- a null-terminated string containing "SECRET"
				  //  PartyUInfo -- the x coordinate of the point in the secret
				  //              (Qe,U )
				  //  PartyVInfo -- the x coordinate of the public key (Qs,V )
				  //  bits -- the number of bits in the digest of HashID
				  // Retrieve seed from KDFe
				  CryptKDFe(decryptKey->publicArea.nameAlg,
					    &eccSecret.x.b,
					    label,
					    &eccPublic.x.b,
					    &decryptKey->publicArea.unique.ecc.x.b,
					    data->t.size * 8,
					    data->t.buffer);
			      }
		      }
	      }
	      break;
#endif  // ALG_ECC
#if !ALG_KEYEDHASH
#  error "KEYEDHASH support is required"
#endif
	  case TPM_ALG_KEYEDHASH:
	    // The seed size can not be bigger than the digest size of nameAlg
	    if(secret->t.size
	       > CryptHashGetDigestSize(decryptKey->publicArea.nameAlg))
		result = TPM_RC_VALUE;
	    else
		{
		    // Retrieve seed by XOR Obfuscation:
		    //    seed = XOR(secret, hash, key, nonceCaller, nullNonce)
		    //    where:
		    //    secret  the secret parameter from the TPM2_StartAuthHMAC
		    //            command that contains the seed value
		    //    hash    nameAlg  of tpmKey
		    //    key     the key or data value in the object referenced by
		    //            entityHandle in the TPM2_StartAuthHMAC command
		    //    nonceCaller the parameter from the TPM2_StartAuthHMAC command
		    //    nullNonce   a zero-length nonce
		    // XOR Obfuscation in place
		    CryptXORObfuscation(decryptKey->publicArea.nameAlg,
					&decryptKey->sensitive.sensitive.bits.b,
					&nonceCaller->b,
					NULL,
					secret->t.size,
					secret->t.secret);
		    // Copy decrypted seed
		    MemoryCopy2B(&data->b, &secret->b, sizeof(data->t.buffer));
		}
	    break;
	  case TPM_ALG_SYMCIPHER:
	      {
		  TPM2B_IV             iv = {{0}};
		  TPMT_SYM_DEF_OBJECT* symDef;
		  // The seed size can not be bigger than the digest size of nameAlg
		  if(secret->t.size
		     > CryptHashGetDigestSize(decryptKey->publicArea.nameAlg))
		      result = TPM_RC_VALUE;
		  else
		      {
			  symDef    = &decryptKey->publicArea.parameters.symDetail.sym;
			  iv.t.size = CryptGetSymmetricBlockSize(symDef->algorithm,
								 symDef->keyBits.sym);
			  if(iv.t.size == 0)
			      return TPM_RC_FAILURE;
			  if(nonceCaller->t.size >= iv.t.size)
			      {
				  MemoryCopy(iv.t.buffer, nonceCaller->t.buffer, iv.t.size);
			      }
			  else
			      {
				  if(nonceCaller->t.size > sizeof(iv.t.buffer))
				      return TPM_RC_FAILURE;
				  MemoryCopy(
					     iv.b.buffer, nonceCaller->t.buffer, nonceCaller->t.size);
			      }
			  // make sure secret will fit
			  if(secret->t.size > sizeof(data->t.buffer))
			      return TPM_RC_FAILURE;
			  data->t.size = secret->t.size;
			  // CFB decrypt, using nonceCaller as iv
			  CryptSymmetricDecrypt(data->t.buffer,
						symDef->algorithm,
						symDef->keyBits.sym,
						decryptKey->sensitive.sensitive.sym.t.buffer,
						&iv,
						TPM_ALG_CFB,
						secret->t.size,
						secret->t.secret);
		      }
	      }
	      break;
	  default:
	    FAIL(FATAL_ERROR_INTERNAL);
	    break;
	}
    return result;
}

//*** CryptParameterEncryption()
// This function does in-place encryption of a response parameter.
void CryptParameterEncryption(
			      TPM_HANDLE handle,             // IN: encrypt session handle
			      TPM2B*     nonceCaller,        // IN: nonce caller
			      INT32      bufferSize,         // IN: size of parameter buffer
			      UINT16     leadingSizeInByte,  // IN: the size of the leading size field in
			      //     bytes
			      TPM2B_AUTH* extraKey,          // IN: additional key material other than
			      //     sessionAuth
			      BYTE* buffer                   // IN/OUT: parameter buffer to be encrypted
			      )
{
    SESSION* session = SessionGet(handle);  // encrypt session
    TPM2B_TYPE(TEMP_KEY,
	       (sizeof(extraKey->t.buffer) + sizeof(session->sessionKey.t.buffer)));
    TPM2B_TEMP_KEY key;             // encryption key
    UINT16         cipherSize = 0;  // size of cipher text

    if(bufferSize < leadingSizeInByte)
	{
	    FAIL(FATAL_ERROR_INTERNAL);
	    return;
	}

    // Parameter encryption for a non-2B is not supported.
    if(leadingSizeInByte != 2)
	{
	    FAIL(FATAL_ERROR_INTERNAL);
	    return;
	}

    // Retrieve encrypted data size.
    if(UINT16_Unmarshal(&cipherSize, &buffer, &bufferSize) != TPM_RC_SUCCESS)
	{
	    FAIL(FATAL_ERROR_INTERNAL);
	    return;
	}

    if(cipherSize > bufferSize)
	{
	    FAIL(FATAL_ERROR_INTERNAL);
	    return;
	}

    // Compute encryption key by concatenating sessionKey with extra key
    MemoryCopy2B(&key.b, &session->sessionKey.b, sizeof(key.t.buffer));
    MemoryConcat2B(&key.b, &extraKey->b, sizeof(key.t.buffer));

    if(session->symmetric.algorithm == TPM_ALG_XOR)

	// XOR parameter encryption formulation:
	//    XOR(parameter, hash, sessionAuth, nonceNewer, nonceOlder)
	CryptXORObfuscation(session->authHashAlg,
			    &(key.b),
			    &(session->nonceTPM.b),
			    nonceCaller,
			    (UINT32)cipherSize,
			    buffer);
    else
	ParmEncryptSym(session->symmetric.algorithm,
		       session->authHashAlg,
		       session->symmetric.keyBits.aes,
		       &(key.b),
		       nonceCaller,
		       &(session->nonceTPM.b),
		       (UINT32)cipherSize,
		       buffer);
    return;
}

//*** CryptParameterDecryption()
// This function does in-place decryption of a command parameter.
//  Return Type: TPM_RC
//      TPM_RC_SIZE             The number of bytes in the input buffer is less than
//                              the number of bytes to be decrypted.
TPM_RC
CryptParameterDecryption(
			 TPM_HANDLE handle,             // IN: encrypted session handle
			 TPM2B*     nonceCaller,        // IN: nonce caller
			 INT32      bufferSize,         // IN: size of parameter buffer
			 UINT16     leadingSizeInByte,  // IN: the size of the leading size field in
			 //     byte
			 TPM2B_AUTH* extraKey,          // IN: the authValue
			 BYTE*       buffer             // IN/OUT: parameter buffer to be decrypted
			 )
{
    SESSION* session = SessionGet(handle);  // encrypt session
    // The HMAC key is going to be the concatenation of the session key and any
    // additional key material (like the authValue). The size of both of these
    // is the size of the buffer which can contain a TPMT_HA.
    TPM2B_TYPE(HMAC_KEY,
	       (sizeof(extraKey->t.buffer) + sizeof(session->sessionKey.t.buffer)));
    TPM2B_HMAC_KEY key;             // decryption key
    UINT16         cipherSize = 0;  // size of ciphertext

    if(bufferSize < leadingSizeInByte)
	{
	    return TPM_RC_INSUFFICIENT;
	}

    // Parameter encryption for a non-2B is not supported.
    if(leadingSizeInByte != 2)
	{
	    FAIL_RC(FATAL_ERROR_INTERNAL);
	}

    // Retrieve encrypted data size.
    if(UINT16_Unmarshal(&cipherSize, &buffer, &bufferSize) != TPM_RC_SUCCESS)
	{
	    return TPM_RC_INSUFFICIENT;
	}

    if(cipherSize > bufferSize)
	{
	    return TPM_RC_SIZE;
	}

    // Compute decryption key by concatenating sessionAuth with extra input key
    MemoryCopy2B(&key.b, &session->sessionKey.b, sizeof(key.t.buffer));
    MemoryConcat2B(&key.b, &extraKey->b, sizeof(key.t.buffer));

    if(session->symmetric.algorithm == TPM_ALG_XOR)
	// XOR parameter decryption formulation:
	//    XOR(parameter, hash, sessionAuth, nonceNewer, nonceOlder)
	// Call XOR obfuscation function
	CryptXORObfuscation(session->authHashAlg,
			    &key.b,
			    nonceCaller,
			    &(session->nonceTPM.b),
			    (UINT32)cipherSize,
			    buffer);
    else
	// Assume that it is one of the symmetric block ciphers.
	ParmDecryptSym(session->symmetric.algorithm,
		       session->authHashAlg,
		       session->symmetric.keyBits.sym,
		       &key.b,
		       nonceCaller,
		       &session->nonceTPM.b,
		       (UINT32)cipherSize,
		       buffer);

    return TPM_RC_SUCCESS;
}

//*** CryptComputeSymmetricUnique()
// This function computes the unique field in public area for symmetric objects.
void CryptComputeSymmetricUnique(
				 TPMT_PUBLIC*    publicArea,  // IN: the object's public area
				 TPMT_SENSITIVE* sensitive,   // IN: the associated sensitive area
				 TPM2B_DIGEST*   unique       // OUT: unique buffer
				 )
{
    // For parents (symmetric and derivation), use an HMAC to compute
    // the 'unique' field
    if(IS_ATTRIBUTE(publicArea->objectAttributes, TPMA_OBJECT, restricted)
       && IS_ATTRIBUTE(publicArea->objectAttributes, TPMA_OBJECT, decrypt))
	{
	    // Unique field is HMAC(sensitive->seedValue, sensitive->sensitive)
	    HMAC_STATE hmacState;
	    unique->b.size = CryptHmacStart2B(
					      &hmacState, publicArea->nameAlg, &sensitive->seedValue.b);
	    CryptDigestUpdate2B(&hmacState.hashState, &sensitive->sensitive.any.b);
	    CryptHmacEnd2B(&hmacState, &unique->b);
	}
    else
	{
	    HASH_STATE hashState;
	    // Unique := Hash(sensitive->seedValue || sensitive->sensitive)
	    unique->t.size = CryptHashStart(&hashState, publicArea->nameAlg);
	    CryptDigestUpdate2B(&hashState, &sensitive->seedValue.b);
	    CryptDigestUpdate2B(&hashState, &sensitive->sensitive.any.b);
	    CryptHashEnd2B(&hashState, &unique->b);
	}
    return;
}

//*** CryptCreateObject()
// This function creates an object.
// For an asymmetric key, it will create a key pair and, for a parent key, a seed
// value for child protections.
//
// For an symmetric object, (TPM_ALG_SYMCIPHER or TPM_ALG_KEYEDHASH), it will
// create a secret key if the caller did not provide one. It will create a random
// secret seed value that is hashed with the secret value to create the public
// unique value.
//
// 'publicArea', 'sensitive', and 'sensitiveCreate' are the only required parameters
// and are the only ones that are used by TPM2_Create(). The other parameters
// are optional and are used when the generated Object needs to be deterministic.
// This is the case for both Primary Objects and Derived Objects.
//
// When a seed value is provided, a RAND_STATE will be populated and used for
// all operations in the object generation that require a random number. In the
// simplest case, TPM2_CreatePrimary() will use 'seed', 'label' and 'context' with
// context being the hash of the template. If the Primary Object is in
// the Endorsement hierarchy, it will also populate 'proof' with ehProof.
//
// For derived keys, 'seed' will be the secret value from the parent, 'label' and
// 'context' will be set according to the parameters of TPM2_CreateLoaded() and
// 'hashAlg' will be set which causes the RAND_STATE to be a KDF generator.
//
//  Return Type: TPM_RC
//      TPM_RC_KEY          a provided key is not an allowed value
//      TPM_RC_KEY_SIZE     key size in the public area does not match the size
//                          in the sensitive creation area for a symmetric key
//      TPM_RC_NO_RESULT    unable to get random values (only in derivation)
//      TPM_RC_RANGE        for an RSA key, the exponent is not supported
//      TPM_RC_SIZE         sensitive data size is larger than allowed for the
//                          scheme for a keyed hash object
//      TPM_RC_VALUE        exponent is not prime or could not find a prime using
//                          the provided parameters for an RSA key;
//                          unsupported name algorithm for an ECC key
TPM_RC
CryptCreateObject(OBJECT*                object,  // IN: new object structure pointer
		  TPMS_SENSITIVE_CREATE* sensitiveCreate,  // IN: sensitive creation
		  RAND_STATE*            rand  // IN: the random number generator
		  //      to use
		  )
{
    TPMT_PUBLIC*    publicArea = &object->publicArea;
    TPMT_SENSITIVE* sensitive  = &object->sensitive;
    TPM_RC          result     = TPM_RC_SUCCESS;
    //
    // Set the sensitive type for the object
    sensitive->sensitiveType = publicArea->type;

    // For all objects, copy the initial authorization data
    sensitive->authValue = sensitiveCreate->userAuth;

    // If the TPM is the source of the data, set the size of the provided data to
    // zero so that there's no confusion about what to do.
    if(IS_ATTRIBUTE(publicArea->objectAttributes, TPMA_OBJECT, sensitiveDataOrigin))
	sensitiveCreate->data.t.size = 0;

    // Generate the key and unique fields for the asymmetric keys and just the
    // sensitive value for symmetric object
    switch(publicArea->type)
	{
#if ALG_RSA
	    // Create RSA key
	  case TPM_ALG_RSA:
	    // RSA uses full object so that it has a place to put the private
	    // exponent
	    result = CryptRsaGenerateKey(publicArea, sensitive, rand);
	    break;
#endif  // ALG_RSA

#if ALG_ECC
	    // Create ECC key
	  case TPM_ALG_ECC:
	    result = CryptEccGenerateKey(publicArea, sensitive, rand);
	    break;
#endif  // ALG_ECC
	  case TPM_ALG_SYMCIPHER:
	    result = CryptGenerateKeySymmetric(
					       publicArea, sensitive, sensitiveCreate, rand);
	    break;
	  case TPM_ALG_KEYEDHASH:
	    result =
		CryptGenerateKeyedHash(publicArea, sensitive, sensitiveCreate, rand);
	    break;
	  default:
	    FAIL(FATAL_ERROR_INTERNAL);
	    break;
	}
    if(result != TPM_RC_SUCCESS)
	return result;
    // Create the sensitive seed value
    // If this is a primary key in the endorsement hierarchy, stir the DRBG state
    // This implementation uses both shProof and ehProof to make sure that there
    // is no leakage of either.
    if(object->attributes.primary && object->attributes.epsHierarchy)
	{
	    DRBG_AdditionalData((DRBG_STATE*)rand, &gp.shProof.b);
	    DRBG_AdditionalData((DRBG_STATE*)rand, &gp.ehProof.b);
	}
    // Generate a seedValue that is the size of the digest produced by nameAlg
    sensitive->seedValue.t.size =
	DRBG_Generate(rand,
		      sensitive->seedValue.t.buffer,
		      CryptHashGetDigestSize(publicArea->nameAlg));
    if(g_inFailureMode)
	return TPM_RC_FAILURE;
    else if(sensitive->seedValue.t.size == 0)
	return TPM_RC_NO_RESULT;
    // For symmetric objects, need to compute the unique value for the public area
    if(publicArea->type == TPM_ALG_SYMCIPHER || publicArea->type == TPM_ALG_KEYEDHASH)
	{
	    CryptComputeSymmetricUnique(publicArea, sensitive, &publicArea->unique.sym);
	}
    else
	{
	    // if this is an asymmetric key and it isn't a parent, then
	    // get rid of the seed.
	    if(IS_ATTRIBUTE(publicArea->objectAttributes, TPMA_OBJECT, sign)
	       || !IS_ATTRIBUTE(publicArea->objectAttributes, TPMA_OBJECT, restricted))
		memset(&sensitive->seedValue, 0, sizeof(sensitive->seedValue));
	}
    // Compute the name
    PublicMarshalAndComputeName(publicArea, &object->name);
    return result;
}

//*** CryptGetSignHashAlg()
// Get the hash algorithm of signature from a TPMT_SIGNATURE structure.
// It assumes the signature is not NULL
//  This is a function for easy access
TPMI_ALG_HASH
CryptGetSignHashAlg(TPMT_SIGNATURE* auth  // IN: signature
		    )
{
    if(auth->sigAlg == TPM_ALG_NULL)
	FAIL(FATAL_ERROR_INTERNAL);

    // Get authHash algorithm based on signing scheme
    switch(auth->sigAlg)
	{
#if ALG_RSA
	    // If RSA is supported, both RSASSA and RSAPSS are required
#  if !defined TPM_ALG_RSASSA || !defined TPM_ALG_RSAPSS
#    error "RSASSA and RSAPSS are required for RSA"
#  endif
	  case TPM_ALG_RSASSA:
	    return auth->signature.rsassa.hash;
	  case TPM_ALG_RSAPSS:
	    return auth->signature.rsapss.hash;
#endif  // ALG_RSA

#if ALG_ECC
	    // If ECC is defined, ECDSA is mandatory
#  if !ALG_ECDSA
#    error "ECDSA is requried for ECC"
#  endif
	  case TPM_ALG_ECDSA:
	    // SM2 and ECSCHNORR are optional

#  if ALG_SM2
	  case TPM_ALG_SM2:
#  endif
#  if ALG_ECSCHNORR
	  case TPM_ALG_ECSCHNORR:
#  endif
	    //all ECC signatures look the same
	    return auth->signature.ecdsa.hash;

#  if ALG_ECDAA
	    // Don't know how to verify an ECDAA signature
	  case TPM_ALG_ECDAA:
	    break;
#  endif

#endif  // ALG_ECC

	  case TPM_ALG_HMAC:
	    return auth->signature.hmac.hashAlg;

	  default:
	    break;
	}
    return TPM_ALG_NULL;
}

//*** CryptIsSplitSign()
// This function us used to determine if the signing operation is a split
// signing operation that required a TPM2_Commit().
//
BOOL CryptIsSplitSign(TPM_ALG_ID scheme  // IN: the algorithm selector
		      )
{
    switch(scheme)
	{
#if ALG_ECDAA
	  case TPM_ALG_ECDAA:
	    return TRUE;
	    break;
#endif  // ALG_ECDAA
	  default:
	    return FALSE;
	    break;
	}
}

//*** CryptIsAsymSignScheme()
// This function indicates if a scheme algorithm is a sign algorithm.
BOOL CryptIsAsymSignScheme(TPMI_ALG_PUBLIC      publicType,  // IN: Type of the object
			   TPMI_ALG_ASYM_SCHEME scheme       // IN: the scheme
			   )
{
    BOOL isSignScheme = TRUE;

    switch(publicType)
	{
#if ALG_RSA
	  case TPM_ALG_RSA:
	    switch(scheme)
		{
#  if !ALG_RSASSA || !ALG_RSAPSS
#    error "RSASSA and PSAPSS required if RSA used."
#  endif
		  case TPM_ALG_RSASSA:
		  case TPM_ALG_RSAPSS:
		    break;
		  default:
		    isSignScheme = FALSE;
		    break;
		}
	    break;
#endif  // ALG_RSA

#if ALG_ECC
	    // If ECC is implemented ECDSA is required
	  case TPM_ALG_ECC:
	    switch(scheme)
		{
		    // Support for ECDSA is required for ECC
		  case TPM_ALG_ECDSA:
#  if ALG_ECDAA  // ECDAA is optional
		  case TPM_ALG_ECDAA:
#  endif
#  if ALG_ECSCHNORR  // Schnorr is also optional
		  case TPM_ALG_ECSCHNORR:
#  endif
#  if ALG_SM2  // SM2 is optional
		  case TPM_ALG_SM2:
#  endif
		    break;
		  default:
		    isSignScheme = FALSE;
		    break;
		}
	    break;
#endif  // ALG_ECC
	  default:
	    isSignScheme = FALSE;
	    break;
	}
    return isSignScheme;
}

//*** CryptIsAsymDecryptScheme()
// This function indicate if a scheme algorithm is a decrypt algorithm.
BOOL CryptIsAsymDecryptScheme(TPMI_ALG_PUBLIC publicType,  // IN: Type of the object
			      TPMI_ALG_ASYM_SCHEME scheme  // IN: the scheme
			      )
{
    BOOL isDecryptScheme = TRUE;

    switch(publicType)
	{
#if ALG_RSA
	  case TPM_ALG_RSA:
	    switch(scheme)
		{
		  case TPM_ALG_RSAES:
		  case TPM_ALG_OAEP:
		    break;
		  default:
		    isDecryptScheme = FALSE;
		    break;
		}
	    break;
#endif  // ALG_RSA

#if ALG_ECC
	    // If ECC is implemented ECDH is required
	  case TPM_ALG_ECC:
	    switch(scheme)
		{
#  if !ALG_ECDH
#    error "ECDH is required for ECC"
#  endif
		  case TPM_ALG_ECDH:
#  if ALG_SM2
		  case TPM_ALG_SM2:
#  endif
#  if ALG_ECMQV
		  case TPM_ALG_ECMQV:
#  endif
		    break;
		  default:
		    isDecryptScheme = FALSE;
		    break;
		}
	    break;
#endif  // ALG_ECC
	  default:
	    isDecryptScheme = FALSE;
	    break;
	}
    return isDecryptScheme;
}

//*** CryptSelectSignScheme()
// This function is used by the attestation and signing commands.  It implements
// the rules for selecting the signature scheme to use in signing. This function
// requires that the signing key either be TPM_RH_NULL or be loaded.
//
// If a default scheme is defined in object, the default scheme should be chosen,
// otherwise, the input scheme should be chosen.
// In the case that  both object and input scheme has a non-NULL scheme
// algorithm, if the schemes are compatible, the input scheme will be chosen.
//
// This function should not be called if 'signObject->publicArea.type' ==
// ALG_SYMCIPHER.
//
//  Return Type: BOOL
//      TRUE(1)         scheme selected
//      FALSE(0)        both 'scheme' and key's default scheme are empty; or
//                      'scheme' is empty while key's default scheme requires
//                      explicit input scheme (split signing); or
//                      non-empty default key scheme differs from 'scheme'
BOOL CryptSelectSignScheme(OBJECT*          signObject,  // IN: signing key
			   TPMT_SIG_SCHEME* scheme       // IN/OUT: signing scheme
			   )
{
    TPMT_SIG_SCHEME* objectScheme;
    TPMT_PUBLIC*     publicArea;
    BOOL             OK;

    // If the signHandle is TPM_RH_NULL, then the NULL scheme is used, regardless
    // of the setting of scheme
    if(signObject == NULL)
	{
	    OK                          = TRUE;
	    scheme->scheme              = TPM_ALG_NULL;
	    scheme->details.any.hashAlg = TPM_ALG_NULL;
	}
    else
	{
	    // assignment to save typing.
	    publicArea = &signObject->publicArea;

	    // A symmetric cipher can be used to encrypt and decrypt but it can't
	    // be used for signing
	    if(publicArea->type == TPM_ALG_SYMCIPHER)
		return FALSE;
	    // Point to the scheme object
	    if(CryptIsAsymAlgorithm(publicArea->type))
		objectScheme =
		    (TPMT_SIG_SCHEME*)&publicArea->parameters.asymDetail.scheme;
	    else
		objectScheme =
		    (TPMT_SIG_SCHEME*)&publicArea->parameters.keyedHashDetail.scheme;

	    // If the object doesn't have a default scheme, then use the
	    // input scheme.
	    if(objectScheme->scheme == TPM_ALG_NULL)
		{
		    // Input and default can't both be NULL
		    OK = (scheme->scheme != TPM_ALG_NULL);
		    // Assume that the scheme is compatible with the key. If not,
		    // an error will be generated in the signing operation.
		}
	    else if(scheme->scheme == TPM_ALG_NULL)
		{
		    // input scheme is NULL so use default

		    // First, check to see if the default requires that the caller
		    // provided scheme data
		    OK = !CryptIsSplitSign(objectScheme->scheme);
		    if(OK)
			{
			    // The object has a scheme and the input is TPM_ALG_NULL so copy
			    // the object scheme as the final scheme. It is better to use a
			    // structure copy than a copy of the individual fields.
			    *scheme = *objectScheme;
			}
		}
	    else
		{
		    // Both input and object have scheme selectors
		    // If the scheme and the hash are not the same then...
		    // NOTE: the reason that there is no copy here is that the input
		    // might contain extra data for a split signing scheme and that
		    // data is not in the object so, it has to be preserved.
		    OK =
			(objectScheme->scheme == scheme->scheme)
			&& (objectScheme->details.any.hashAlg == scheme->details.any.hashAlg);
		}
	}
    return OK;
}

//*** CryptSign()
// Sign a digest with asymmetric key or HMAC.
// This function is called by attestation commands and the generic TPM2_Sign
// command.
// This function checks the key scheme and digest size.  It does not
// check if the sign operation is allowed for restricted key.  It should be
// checked before the function is called.
// The function will assert if the key is not a signing key.
//
//  Return Type: TPM_RC
//      TPM_RC_SCHEME      'signScheme' is not compatible with the signing key type
//      TPM_RC_VALUE       'digest' value is greater than the modulus of
//                         'signHandle' or size of 'hashData' does not match hash
//                         algorithm in'signScheme' (for an RSA key);
//                         invalid commit status or failed to generate "r" value
//                         (for an ECC key)
TPM_RC
CryptSign(OBJECT*          signKey,     // IN: signing key
	  TPMT_SIG_SCHEME* signScheme,  // IN: sign scheme.
	  TPM2B_DIGEST*    digest,      // IN: The digest being signed
	  TPMT_SIGNATURE*  signature    // OUT: signature
	  )
{
    TPM_RC result = TPM_RC_SCHEME;

    // Initialize signature scheme
    signature->sigAlg = signScheme->scheme;

    // If the signature algorithm is TPM_ALG_NULL or the signing key is NULL,
    // then we are done
    if((signature->sigAlg == TPM_ALG_NULL) || (signKey == NULL))
	return TPM_RC_SUCCESS;

    // Initialize signature hash
    // Note: need to do the check for TPM_ALG_NULL first because the null scheme
    // doesn't have a hashAlg member.
    signature->signature.any.hashAlg = signScheme->details.any.hashAlg;

    // perform sign operation based on different key type
    switch(signKey->publicArea.type)
	{
#if ALG_RSA
	  case TPM_ALG_RSA:
	    result = CryptRsaSign(signature, signKey, digest, NULL);
	    break;
#endif  // ALG_RSA
#if ALG_ECC
	  case TPM_ALG_ECC:
	    // The reason that signScheme is passed to CryptEccSign but not to the
	    // other signing methods is that the signing for ECC may be split and
	    // need the 'r' value that is in the scheme but not in the signature.
	    result = CryptEccSign(
				  signature, signKey, digest, (TPMT_ECC_SCHEME*)signScheme, NULL);
	    break;
#endif  // ALG_ECC
	  case TPM_ALG_KEYEDHASH:
	    result = CryptHmacSign(signature, signKey, digest);
	    break;
	  default:
	    FAIL(FATAL_ERROR_INTERNAL);
	    break;
	}
    return result;
}

//*** CryptValidateSignature()
// This function is used to verify a signature.  It is called by
// TPM2_VerifySignature() and TPM2_PolicySigned.
//
// Since this operation only requires use of a public key, no consistency
// checks are necessary for the key to signature type because a caller can load
// any public key that they like with any scheme that they like. This routine
// simply makes sure that the signature is correct, whatever the type.
//
//  Return Type: TPM_RC
//      TPM_RC_SIGNATURE            the signature is not genuine
//      TPM_RC_SCHEME               the scheme is not supported
//      TPM_RC_HANDLE               an HMAC key was selected but the
//                                  private part of the key is not loaded
TPM_RC
CryptValidateSignature(TPMI_DH_OBJECT  keyHandle,  // IN: The handle of sign key
		       TPM2B_DIGEST*   digest,     // IN: The digest being validated
		       TPMT_SIGNATURE* signature   // IN: signature
		       )
{
    // NOTE: HandleToObject will either return a pointer to a loaded object or
    // will assert. It will never return a non-valid value. This makes it save
    // to initialize 'publicArea' with the return value from HandleToObject()
    // without checking it first.
    OBJECT*      signObject = HandleToObject(keyHandle);
    TPMT_PUBLIC* publicArea = &signObject->publicArea;
    TPM_RC       result     = TPM_RC_SCHEME;

    // The input unmarshaling should prevent any input signature from being
    // a NULL signature, but just in case
    if(signature->sigAlg == TPM_ALG_NULL)
	return TPM_RC_SIGNATURE;

    switch(publicArea->type)
	{
#if ALG_RSA
	  case TPM_ALG_RSA:
	      {
		  //
		  // Call RSA code to verify signature
		  result = CryptRsaValidateSignature(signature, signObject, digest);
		  break;
	      }
#endif  // ALG_RSA

#if ALG_ECC
	  case TPM_ALG_ECC:
	    result = CryptEccValidateSignature(signature, signObject, digest);
	    break;
#endif  // ALG_ECC

	  case TPM_ALG_KEYEDHASH:
	    if(signObject->attributes.publicOnly)
		result = TPM_RCS_HANDLE;
	    else
		result = CryptHMACVerifySignature(signObject, digest, signature);
	    break;
	  default:
	    break;
	}
    return result;
}

//*** CryptGetTestResult
// This function returns the results of a self-test function.
// Note: the behavior in this function is NOT the correct behavior for a real
// TPM implementation.  An artificial behavior is placed here due to the
// limitation of a software simulation environment.  For the correct behavior,
// consult the part 3 specification for TPM2_GetTestResult().
TPM_RC
CryptGetTestResult(TPM2B_MAX_BUFFER* outData  // OUT: test result data
		   )
{
    outData->t.size = 0;
    return TPM_RC_SUCCESS;
}

//*** CryptValidateKeys()
// This function is used to verify that the key material of and object is valid.
// For a 'publicOnly' object, the key is verified for size and, if it is an ECC
// key, it is verified to be on the specified curve. For a key with a sensitive
// area, the binding between the public and private parts of the key are verified.
// If the nameAlg of the key is TPM_ALG_NULL, then the size of the sensitive area
// is verified but the public portion is not verified, unless the key is an RSA key.
// For an RSA key, the reason for loading the sensitive area is to use it. The
// only way to use a private RSA key is to compute the private exponent. To compute
// the private exponent, the public modulus is used.
//  Return Type: TPM_RC
//      TPM_RC_BINDING      the public and private parts are not cryptographically
//                          bound
//      TPM_RC_HASH         cannot have a publicOnly key with nameAlg of TPM_ALG_NULL
//      TPM_RC_KEY          the public unique is not valid
//      TPM_RC_KEY_SIZE     the private area key is not valid
//      TPM_RC_TYPE         the types of the sensitive and private parts do not match
TPM_RC
CryptValidateKeys(TPMT_PUBLIC*    publicArea,
		  TPMT_SENSITIVE* sensitive,
		  TPM_RC          blamePublic,
		  TPM_RC          blameSensitive)
{
    TPM_RC             result;
    UINT16             keySizeInBytes;
    UINT16             digestSize = CryptHashGetDigestSize(publicArea->nameAlg);
    TPMU_PUBLIC_PARMS* params     = &publicArea->parameters;
    TPMU_PUBLIC_ID*    unique     = &publicArea->unique;

    if(sensitive != NULL)
	{
	    // Make sure that the types of the public and sensitive are compatible
	    if(publicArea->type != sensitive->sensitiveType)
		return TPM_RCS_TYPE + blameSensitive;
	    // Make sure that the authValue is not bigger than allowed
	    // If there is no name algorithm, then the size just needs to be less than
	    // the maximum size of the buffer used for authorization. That size check
	    // was made during unmarshaling of the sensitive area
	    if((sensitive->authValue.t.size) > digestSize && (digestSize > 0))
		return TPM_RCS_SIZE + blameSensitive;
	}
    switch(publicArea->type)
	{
#if ALG_RSA
	  case TPM_ALG_RSA:
	    keySizeInBytes = BITS_TO_BYTES(params->rsaDetail.keyBits);

	    // Regardless of whether there is a sensitive area, the public modulus
	    // needs to have the correct size. Otherwise, it can't be used for
	    // any public key operation nor can it be used to compute the private
	    // exponent.
	    // NOTE: This implementation only supports key sizes that are multiples
	    // of 1024 bits which means that the MSb of the 0th byte will always be
	    // SET in any prime and in the public modulus.
	    if((unique->rsa.t.size != keySizeInBytes)
	       || (unique->rsa.t.buffer[0] < 0x80))
		return TPM_RCS_KEY + blamePublic;
	    if(params->rsaDetail.exponent != 0 && params->rsaDetail.exponent < 7)
		return TPM_RCS_VALUE + blamePublic;
	    if(sensitive != NULL)
		{
		    // If there is a sensitive area, it has to be the correct size
		    // including having the correct high order bit SET.
		    if(((sensitive->sensitive.rsa.t.size * 2) != keySizeInBytes)
		       || (sensitive->sensitive.rsa.t.buffer[0] < 0x80))
			return TPM_RCS_KEY_SIZE + blameSensitive;
		}
	    break;
#endif
#if ALG_ECC
	  case TPM_ALG_ECC:
	      {
		  TPMI_ECC_CURVE curveId;
		  curveId        = params->eccDetail.curveID;
		  keySizeInBytes = BITS_TO_BYTES(CryptEccGetKeySizeForCurve(curveId));
		  if(sensitive == NULL)
		      {
			  // Validate the public key size
			  if(unique->ecc.x.t.size != keySizeInBytes
			     || unique->ecc.y.t.size != keySizeInBytes)
			      return TPM_RCS_KEY + blamePublic;
			  if(publicArea->nameAlg != TPM_ALG_NULL)
			      {
				  if(!CryptEccIsPointOnCurve(curveId, &unique->ecc))
				      return TPM_RCS_ECC_POINT + blamePublic;
			      }
		      }
		  else
		      {
			  // If the nameAlg is TPM_ALG_NULL, then only verify that the
			  // private part of the key is OK.
			  if(!CryptEccIsValidPrivateKey(&sensitive->sensitive.ecc, curveId))
			      return TPM_RCS_KEY_SIZE;
			  if(publicArea->nameAlg != TPM_ALG_NULL)
			      {
				  // Full key load, verify that the public point belongs to the
				  // private key.
				  TPMS_ECC_POINT toCompare;
				  result = CryptEccPointMultiply(&toCompare,
								 curveId,
								 NULL,
								 &sensitive->sensitive.ecc,
								 NULL,
								 NULL);
				  if(result != TPM_RC_SUCCESS)
				      return TPM_RCS_BINDING;
				  else
				      {
					  // Make sure that the private key generated the public key.
					  // The input values and the values produced by the point
					  // multiply may not be the same size so adjust the computed
					  // value to match the size of the input value by adding or
					  // removing zeros.
					  AdjustNumberB(&toCompare.x.b, unique->ecc.x.t.size);
					  AdjustNumberB(&toCompare.y.b, unique->ecc.y.t.size);
					  if(!MemoryEqual2B(&unique->ecc.x.b, &toCompare.x.b)
					     || !MemoryEqual2B(&unique->ecc.y.b, &toCompare.y.b))
					      return TPM_RCS_BINDING;
				      }
			      }
		      }
		  break;
	      }
#endif
	  default:
	    // Checks for SYMCIPHER and KEYEDHASH are largely the same
	    // If public area has a nameAlg, then validate the public area size
	    // and if there is also a sensitive area, validate the binding

	    // For consistency, if the object is public-only just make sure that
	    // the unique field is consistent with the name algorithm
	    if(sensitive == NULL)
		{
		    if(unique->sym.t.size != digestSize)
			return TPM_RCS_KEY + blamePublic;
		}
	    else
		{
		    // Make sure that the key size in the sensitive area is consistent.
		    if(publicArea->type == TPM_ALG_SYMCIPHER)
			{
			    result = CryptSymKeyValidate(&params->symDetail.sym,
							 &sensitive->sensitive.sym);
			    if(result != TPM_RC_SUCCESS)
				return result + blameSensitive;
			}
		    else
			{
			    // For a keyed hash object, the key has to be less than the
			    // smaller of the block size of the hash used in the scheme or
			    // 128 bytes. The worst case value is limited by the
			    // unmarshaling code so the only thing left to be checked is
			    // that it does not exceed the block size of the hash.
			    // by the hash algorithm of the scheme.
			    TPMT_KEYEDHASH_SCHEME* scheme;
			    UINT16                 maxSize;
			    scheme = &params->keyedHashDetail.scheme;
			    if(scheme->scheme == TPM_ALG_XOR)
				{
				    maxSize = CryptHashGetBlockSize(scheme->details.xorr.hashAlg);
				}
			    else if(scheme->scheme == TPM_ALG_HMAC)
				{
				    maxSize = CryptHashGetBlockSize(scheme->details.hmac.hashAlg);
				}
			    else if(scheme->scheme == TPM_ALG_NULL)
				{
				    // Not signing or xor so must be a data block
				    maxSize = 128;
				}
			    else
				return TPM_RCS_SCHEME + blamePublic;
			    if(sensitive->sensitive.bits.t.size > maxSize)
				return TPM_RCS_KEY_SIZE + blameSensitive;
			}
		    // If there is a nameAlg, check the binding
		    if(publicArea->nameAlg != TPM_ALG_NULL)
			{
			    TPM2B_DIGEST compare;
			    if(sensitive->seedValue.t.size != digestSize)
				return TPM_RCS_KEY_SIZE + blameSensitive;

			    CryptComputeSymmetricUnique(publicArea, sensitive, &compare);
			    if(!MemoryEqual2B(&unique->sym.b, &compare.b))
				return TPM_RC_BINDING;
			}
		}
	    break;
	}
    // For a parent, need to check that the seedValue is the correct size for
    // protections. It should be at least half the size of the nameAlg
    if(IS_ATTRIBUTE(publicArea->objectAttributes, TPMA_OBJECT, restricted)
       && IS_ATTRIBUTE(publicArea->objectAttributes, TPMA_OBJECT, decrypt)
       && sensitive != NULL && publicArea->nameAlg != TPM_ALG_NULL)
	{
	    if((sensitive->seedValue.t.size < (digestSize / 2))
	       || (sensitive->seedValue.t.size > digestSize))
		return TPM_RCS_SIZE + blameSensitive;
	}
    return TPM_RC_SUCCESS;
}

//*** CryptSelectMac()
// This function is used to set the MAC scheme based on the key parameters and
// the input scheme.
//  Return Type: TPM_RC
//      TPM_RC_SCHEME       the scheme is not a valid mac scheme
//      TPM_RC_TYPE         the input key is not a type that supports a mac
//      TPM_RC_VALUE        the input scheme and the key scheme are not compatible
TPM_RC
CryptSelectMac(TPMT_PUBLIC* publicArea, TPMI_ALG_MAC_SCHEME* inMac)
{
    TPM_ALG_ID macAlg = TPM_ALG_NULL;
    switch(publicArea->type)
	{
	  case TPM_ALG_KEYEDHASH:
	      {
		  // Local value to keep lines from getting too long
		  TPMT_KEYEDHASH_SCHEME* scheme;
		  scheme = &publicArea->parameters.keyedHashDetail.scheme;
		  // Expect that the scheme is either HMAC or NULL
		  if(scheme->scheme != TPM_ALG_NULL)
		      macAlg = scheme->details.hmac.hashAlg;
		  break;
	      }
	  case TPM_ALG_SYMCIPHER:
	      {
		  TPMT_SYM_DEF_OBJECT* scheme;
		  scheme = &publicArea->parameters.symDetail.sym;
		  // Expect that the scheme is either valid symmetric cipher or NULL
		  if(scheme->algorithm != TPM_ALG_NULL)
		      macAlg = scheme->mode.sym;
		  break;
	      }
	  default:
	    return TPM_RCS_TYPE;
	}
    // If the input value is not TPM_ALG_NULL ...
    if(*inMac != TPM_ALG_NULL)
	{
	    // ... then either the scheme in the key must be TPM_ALG_NULL or the input
	    // value must match
	    if((macAlg != TPM_ALG_NULL) && (*inMac != macAlg))
		return TPM_RCS_VALUE;
	}
    else
	{
	    // Since the input value is TPM_ALG_NULL, then the key value can't be
	    // TPM_ALG_NULL
	    if(macAlg == TPM_ALG_NULL)
		return TPM_RCS_VALUE;
	    *inMac = macAlg;
	}
    if(!CryptMacIsValidForKey(publicArea->type, *inMac, FALSE))
	return TPM_RCS_SCHEME;
    return TPM_RC_SUCCESS;
}

//*** CryptMacIsValidForKey()
// Check to see if the key type is compatible with the mac type
BOOL CryptMacIsValidForKey(TPM_ALG_ID keyType, TPM_ALG_ID macAlg, BOOL flag)
{
    switch(keyType)
	{
	  case TPM_ALG_KEYEDHASH:
	    return CryptHashIsValidAlg(macAlg, flag);
	    break;
	  case TPM_ALG_SYMCIPHER:
	    return CryptSmacIsValidAlg(macAlg, flag);
	    break;
	  default:
	    break;
	}
    return FALSE;
}

//*** CryptSmacIsValidAlg()
// This function is used to test if an algorithm is a supported SMAC algorithm. It
// needs to be updated as new algorithms are added.
BOOL CryptSmacIsValidAlg(TPM_ALG_ID alg,
			 BOOL       FLAG  // IN: Indicates if TPM_ALG_NULL is valid
			 )
{
    switch(alg)
	{
#if ALG_CMAC
	  case TPM_ALG_CMAC:
	    return TRUE;
	    break;
#endif
	  case TPM_ALG_NULL:
	    return FLAG;
	    break;
	  default:
	    return FALSE;
	}
}

//*** CryptSymModeIsValid()
// Function checks to see if an algorithm ID is a valid, symmetric block cipher
// mode for the TPM. If 'flag' is SET, them TPM_ALG_NULL is a valid mode.
// not include the modes used for SMAC
BOOL CryptSymModeIsValid(TPM_ALG_ID mode, BOOL flag)
{
    switch(mode)
	{
#if ALG_CTR
	  case TPM_ALG_CTR:
#endif  // ALG_CTR
#if ALG_OFB
	  case TPM_ALG_OFB:
#endif  // ALG_OFB
#if ALG_CBC
	  case TPM_ALG_CBC:
#endif  // ALG_CBC
#if ALG_CFB
	  case TPM_ALG_CFB:
#endif  // ALG_CFB
#if ALG_ECB
	  case TPM_ALG_ECB:
#endif  // ALG_ECB
	    return TRUE;
	  case TPM_ALG_NULL:
	    return flag;
	    break;
	  default:
	    break;
	}
    return FALSE;
}
